Open
Conversation
Replace hand-rolled i18n with i18next + react-i18next so that adding a new language only requires dropping a JSON file in lib/i18n/locales/. - Add i18next, react-i18next, i18next-browser-languagedetector deps - Generate zh-CN.json / en-US.json from existing TS translation modules - Rewrite lib/i18n/index.ts as a thin wrapper around i18n.t() - Rewrite use-i18n hook to delegate to useTranslation(); external API (locale, setLocale, t) is unchanged so consumers need no changes - SSR-safe: LanguageDetector only loaded on client side Closes #327 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Replace string concatenation (greeting + displayName) with two
i18next keys: greetingWithName (with {{name}} interpolation) and
greetingDefault (standalone, no name).
This lets each locale choose natural phrasing independently:
- zh-CN: "嗨,同学" / "嗨,Alice"
- en-US: "Hi there" / "Hi, Alice"
- Future locales can avoid gender issues by choosing genderless defaults
Also widen the t() type signature to accept interpolation options.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Replace hardcoded zh-CN/en-US imports with i18next-resources-to-backend
and dynamic import(`./locales/${language}.json`). Bundler scans the
locales/ directory at build time, so adding a new language now requires
only dropping a JSON file — zero changes to existing code.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
LanguageDetector ran during i18next init(), detecting browser language before React hydrated — server rendered zh-CN while client switched to en-US immediately, causing a hydration mismatch. Fix: remove i18next-browser-languagedetector; init with a fixed lng (zh-CN) so server and client agree on the first render. Language detection is now done in I18nProvider's useEffect after hydration. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Replace manual locale validation and startsWith('zh') prefix matching
with i18next's built-in fallback mechanism. Now changeLanguage() is
called with navigator.language directly — if the exact locale has no
JSON file, i18next automatically falls back to fallbackLng.
Also widen Locale type from union to string so adding new languages
doesn't require modifying types.ts.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Add lib/i18n/course-languages.ts with curated language list
(zh-CN, zh-TW, en-US, ja, ko, fr, de, es, pt, ru, ar) including
native labels and English prompt names
- Course language defaults to UI locale on first visit; once user
explicitly picks a language, that choice persists across sessions
- Replace toggle button with dropdown selector showing native labels
- Widen language types from 'zh-CN'|'en-US' to string throughout
- Fix hardcoded language ternaries in LLM prompt injection:
- prompt-builder.ts: use getCourseLanguagePromptName()
- classroom-generation.ts: remove normalizeLanguage() that forced
all non-English to zh-CN
- PBL system prompt, agent templates, generate-pbl: append language
instruction for non-zh/en languages
- quiz-grade API: add language suffix for grading feedback
Closes #327
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
quiz-view was passing the UI locale to the grading API, causing AI feedback to follow the student's answer language instead of the course language. Now reads stage.language from the store. Also strengthen the grading prompt: explicitly instruct the LLM to write comments in the course language regardless of the student's input language. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
These files are replaced by lib/i18n/locales/*.json and are no longer imported anywhere in the codebase. Removed: chat.ts, common.ts, generation.ts, settings.ts, stage.ts Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…e registry Add lib/i18n/locales.ts as a single source of truth for supported languages. Language selectors in header and homepage now render dynamically from this registry. Also adds supportedLngs to i18next config so unsupported languages fall back correctly. Adding a new language now only requires a JSON file and one line in the registry. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Resolve modify/delete conflict on lib/i18n/generation.ts — this file was removed in the i18next branch (translations migrated to JSON locale files) and modified on main. All new keys from main are already present in the JSON locales, so the TS file is no longer needed. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add classroom.rename, classroom.renamePlaceholder, and classroom.renameFailed that were added to generation.ts on main but missing from the JSON locale files. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…cale resolution - Extract shared <LanguageSwitcher /> component from page.tsx and header.tsx - Derive Locale type from supportedLocales registry (as const satisfies) - Add resolveLocale() to match browser language prefixes (e.g. 'en' → 'en-US') - Remove config.ts changes (nonExplicitSupportedLngs + inline resources) that broke translation loading when combined with resourcesToBackend Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Collaborator
Author
What this PR doesMigrates the hand-rolled i18n system to i18next + react-i18next. The external API ( Key changes
|
…replace()
- Convert all {var} placeholders in locale JSONs to {{var}} (i18next default)
- Replace .replace('{var}', value) calls with t(key, {var: value}) in all
consuming components: lecture-notes-view, pbl-renderer, use-pbl-chat,
generation-preview, whiteboard-history, agent-settings
- Widen t() type signature in handleIssueComplete to accept interpolation options
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Remove greetingDefault key; greeting always uses greetingWithName with displayName (falls back to profile.defaultNickname) - Change en-US defaultNickname from "Student" to "Learner" - Add TRANSLATION_GUIDE.md documenting how to add languages and explaining keys with non-obvious UX design intent Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Collaborator
Author
Updates since last commentFix: interpolation placeholders (93f5e42)The JSON locale files used single-brace
Improvement: unify greeting with default nickname (f365c75)Previously
New: translation guide (f365c75)Added
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Migrate the hand-rolled i18n system to i18next + react-i18next. Adding a new language now only requires dropping a JSON file in
lib/i18n/locales/— zero changes to existing code.Related Issues
Closes #327
Changes
i18next,react-i18next,i18next-resources-to-backenddependencieszh-CN.json,en-US.json)lib/i18n/index.tsas a thin wrapper aroundi18n.t()use-i18nhook to delegate touseTranslation(); external API (locale,setLocale,t) unchanged — all 59+ consumer files need no changesimport()— bundler scanslocales/at build timeuseEffectafter hydration (fixes SSR mismatch)greetingWithName/greetingDefault) to support natural phrasing across languagesLocaletype from'zh-CN' | 'en-US'tostringfor extensibilityType of Change
Verification
Steps to reproduce / test
pnpm dev)What you personally verified
Evidence
pnpm format && pnpm lint --fix && npx tsc --noEmit)Checklist